package fr.babos.graphic.components.controls.text 
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.events.Event;
	import flash.events.FocusEvent;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.events.SoftKeyboardEvent;
	import flash.events.SoftKeyboardTrigger;
	import flash.events.TouchEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.text.AutoCapitalize;
	import flash.text.engine.FontPosture;
	import flash.text.engine.FontWeight;
	import flash.text.SoftKeyboardType;
	import flash.text.StageText;
	import flash.text.StageTextInitOptions;
	import flash.text.TextFormat;
	import flash.text.TextFormatAlign;
	import fr.babos.graphic.components.base.ResizableComponent;
	import fr.babos.utils.ScreenUtils;
	
	/**
	 * ...
	 * @author ZoulouX
	 */
	public class NativeTextInput extends ResizableComponent
	{
		/**
		 * Le champ texte natif
		 */
		protected var _stageText				:StageText;
		
		/**
		 * Si le champ texte est rendu en bitmap
		 */
		protected var _rasterize				:Boolean;
		
		/**
		 * Si on doit convertir le champ texte en bitmap automatiquement (lorsqu'il n'est pas focus)
		 */
		protected var _autoRasterize			:Boolean;
		
		/**
		 * Le bitmap qui va servire à raterize
		 */
		protected var _rasterizeBitmap			:Bitmap;
		
		
		/**
		 * Visibilité du composant
		 */
		override public function get visible ():Boolean { return super.visible; }
		override public function set visible (value:Boolean):void
		{
			// Relayer
			super.visible = value;
			
			// Appliquer
			refreshPosition();
		}
		
		/**
		 * Le champ texte natif
		 */
		public function get stageText ():StageText { return _stageText; }
		
		/**
		 * Si le champ texte est rendu en bitmap
		 */
		public function get rasterize ():Boolean { return _rasterize; }
		public function set rasterize (value:Boolean):void
		{
			// Si c'est différent
			if (_rasterize != value)
			{
				// Enregistrer
				_rasterize = value;
				
				// Si on passe en mode rasterize
				if (_rasterize)
				{
					// On créé le bitmap porteur, son bitmapData à la taille de la webview
					_rasterizeBitmap = new Bitmap();
					
					// Actualiser le raster
					updateRaster();
					
					// On ajoute le bitmap porteur
					addChild(_rasterizeBitmap);
				}
				else
				{
					// On supprime le bitmap porteur et son bitmapData
					removeChild(_rasterizeBitmap);
					_rasterizeBitmap.bitmapData.dispose();
					_rasterizeBitmap = null;
				}
				
				// Actualiser
				refreshPosition();
			}
		}
		
		/**
		 * Si on doit convertir le champ texte en bitmap automatiquement (lorsqu'il n'est pas focus)
		 */
		public function get autoRasterize ():Boolean { return _autoRasterize; }
		public function set autoRasterize (value:Boolean):void
		{
			// Enregistrer
			_autoRasterize = value;
			
			// Passer en rasterize si on est en auto
			rasterize = _autoRasterize;
			
			// Actualiser le rendu bitmap
			updateRaster();
		}
		
		/**
		 * Le texte affiché par le champ texte
		 */
		public function get text ():String { return _stageText.text; }
		public function set text (value:String):void
		{
			// Appliquer sur le champ texte
			_stageText.text = value;
			
			// Actualiser le rendu bitmap
			updateRaster();
		}
		
		/**
		 * Créer un champ input natif. Ce champ natif s'intègre dans le flux de composants ResizableComponent.
		 * @param	pMultiline : Si le texte est multiligne (sinon un croix apparaitra à droite sur iOS)
		 * @param	pAutoCorrect : Si la traduction automatique doit être activée
		 * @param	pAutoCapitalize : Si les majuscules automatiques doivent être activées (voir classe AutoCapitalize pour les valeurs)
		 * @param	pPassword : Si on doit afficher ce champ en tant que mot de passe (petites étoiles)
		 * @param	pRestrict : Les caractères autorisés, string au format regex (laisser null pour tout autoriser)
		 * @param	pReturnLabel : Le label du bouton Return sur le clavier virtuel (laisser null pour valeur par défaut)
		 */
		public function NativeTextInput (pMultiline:Boolean = false, pAutoCorrect:Boolean = false, pAutoCapitalize:String = "none", pPassword:Boolean = false, pRestrict:String = null, pReturnLabel:String = null)
		{
			// Créer le champ texte
			_stageText = new StageText(new StageTextInitOptions(pMultiline));
			
			// Paramètres du champ texte
			_stageText.autoCorrect = pAutoCorrect;
			_stageText.autoCapitalize = pAutoCapitalize;
			_stageText.displayAsPassword = pPassword;
			
			// Restriction de caractères
			if (pRestrict != null)
				_stageText.restrict = pRestrict;
			
			// Label du bouton Return du clavier virtuel
			if (pReturnLabel != null)
				_stageText.returnKeyLabel = pReturnLabel;
			
			// Ecouter le clavier virtuel
			_stageText.addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATE, softKeyboardEventHandler);
			_stageText.addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATING, softKeyboardEventHandler);
			_stageText.addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_DEACTIVATE, softKeyboardEventHandler);
			
			// Ecouter les changements de données / interaction / focus
			/*_stageText.addEventListener(Event.CHANGE, stageTextHandler);
			_stageText.addEventListener(FocusEvent.FOCUS_IN, stageTextHandler);
			_stageText.addEventListener(FocusEvent.FOCUS_OUT, stageTextHandler);
			_stageText.addEventListener(KeyboardEvent.KEY_DOWN, stageTextHandler);
			_stageText.addEventListener(KeyboardEvent.KEY_UP, stageTextHandler);*/
			
			// Ecouter les clics sur ce composant
			//addEventListener(MouseEvent.MOUSE_DOWN, clickHandler);
			addEventListener(TouchEvent.TOUCH_TAP, clickHandler);
			
			// Ne pas activer les clics sur les enfants
			mouseChildren = false;
		}
		
		/**
		 * Clic sur ce composant
		 */
		protected function clickHandler (event:Event):void 
		{
			// Si on est en autorasterize et qu'on est en mode bitmap
			if (_autoRasterize && _rasterize)
			{
				// On désactive le mode bitmap
				rasterize = false;
				
				// Et on assigne le focus au champ texte
				_stageText.assignFocus();
			}
		}
		
		/**
		 * Evènement d'intération sur le champ texte
		 */
		protected function stageTextHandler (event:Event):void 
		{
			// Si c'est une sortie de focus est qu'on est en autoRasterize
			if (event.type == FocusEvent.FOCUS_OUT && _autoRasterize)
			{
				// On active le mode de rendu bitmap
				rasterize = true;
			}
			
			// Relayer tous les events
			// Attentions, ces events ne bubble pas à travers la displayList, il faut écouter directement cet objet
			dispatchEvent(event);
		}
		
		/**
		 * Evènement sur le clavier virtuel
		 */
		protected function softKeyboardEventHandler (event:SoftKeyboardEvent):void
		{
			// Relayer sur le stage pour que le wrapper puisse les écouter
			_stageText.stage.dispatchEvent(event);
			
			// Actualiser la position
			refreshPosition();
		}
		
		/**
		 * Initialisation
		 */
		override public function init ():void
		{
			// Récupérer le stage pour le champ texte
			_stageText.stage = stage;
			
			// Actualiser la position
			refreshPosition();
			
			// Actualiser le raster
			updateRaster();
		}
		
		/**
		 * Actualisation de la position
		 * @param	pNeedResized
		 */
		override protected function needReplace (pNeedResized:Boolean = false):void
		{
			// Relayer
			super.needReplace(pNeedResized);
			
			// Réappliquer au stageWebView
			refreshPosition();
			
			// Si on est en autoRasterize
			if (_autoRasterize)
			{
				// On rasterize
				updateRaster();
			}
		}
		
		/**
		 * Actualiser le bitmap du raster
		 */
		public function updateRaster ():void
		{
			// Si on est en mode de rendu bitmap
			if (_rasterize && _stageText != null && _stageText.stage != null)
			{
				// Vérifier si la taille a changé ou si on n'a pas de bitmapData
				if (_rasterizeBitmap.bitmapData == null || _rasterizeBitmap.bitmapData.width != _stageText.viewPort.width || _rasterizeBitmap.bitmapData.height != _stageText.viewPort.height)
				{
					// Virer l'ancien bitmapData si besoin
					if (_rasterizeBitmap.bitmapData != null)
						_rasterizeBitmap.bitmapData.dispose();
					
					// Actualiser le bitmapData
					_rasterizeBitmap.bitmapData = new BitmapData(_stageText.viewPort.width, _stageText.viewPort.height, true, 0);
				}
				
				trace(_stageText, _stageText.stage, _stageText.viewPort, _rasterizeBitmap.bitmapData, _rasterizeBitmap.bitmapData.width, _rasterizeBitmap.bitmapData.height);
				
				// Dessiner le stageText dans le bitmapData
				_stageText.drawViewPortToBitmapData(_rasterizeBitmap.bitmapData);
				
				// Appliquer la taille au bitmap
				_rasterizeBitmap.width = _localWidth;
				_rasterizeBitmap.height = _localHeight;
			}
		}
		
		/**
		 * Actualiser la position
		 */
		protected function refreshPosition ():void
		{
			// Si on a un stageText
			if (_stageText != null && _localWidth > 0 && _localHeight > 0)
			{
				// Récupérer le ratio du stage
				var ratio:Number = ScreenUtils.getRatioForStage(stage);
				
				// Si ce text est visible
				if (super.visible && !_rasterize)
				{
					// Récupérer le point de référence de la position de notre clip
					var registerPoint:Point = new Point();
					registerPoint = localToGlobal(registerPoint);
					
					// On lui applique un viewPort
					_stageText.viewPort = new Rectangle(
						int(registerPoint.x - .5),
						int(registerPoint.y - .5),
						int(_localWidth * ratio - .5),
						int(_localHeight * ratio - .5)
					);
				}
				else
				{
					// Sinon on sort simplement la vue de la zone visible
					_stageText.viewPort = new Rectangle(
						- int(_localWidth * ratio - .5) - 1,
						- int(_localHeight * ratio - .5) - 1,
						int(_localWidth * ratio - .5),
						int(_localHeight * ratio - .5)
					);
				}
			}
		}
		
		/**
		 * Définir le format du texte
		 * @param	pFormat : Le format
		 */
		public function format (pFormat:TextFormat):NativeTextInput
		{
			// Appliquer les paramètres que l'on peut
			_stageText.fontFamily = pFormat.font;
			_stageText.fontPosture = (pFormat.italic == null || !pFormat.italic ? FontPosture.NORMAL : FontPosture.ITALIC);
			_stageText.fontWeight = (pFormat.bold == null || !pFormat.bold ? FontWeight.NORMAL : FontWeight.BOLD)
			_stageText.color = (pFormat.color == null ? 0 : parseFloat(pFormat.color.toString()));
			_stageText.fontSize = (pFormat.size == null ? 12 : parseFloat(pFormat.size.toString()));
			_stageText.textAlign = (pFormat.align == null ? TextFormatAlign.LEFT : pFormat.align);
			
			// Méthode chaînable
			return this;
		}
		
		/**
		 * Définir le textSkin sur ce textBase
		 */
		public function textSkin (pTextSkin:TextSkin):NativeTextInput
		{
			// Définir le format / style / rendu de font
			format(pTextSkin.textFormat);
			
			// Méthode chaînable
			return this;
		}
		
		/**
		 * Destruction
		 */
		override public function dispose ():void
		{
			// Ne plus écouter
			_stageText.removeEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATE, softKeyboardEventHandler);
			_stageText.removeEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATING, softKeyboardEventHandler);
			_stageText.removeEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_DEACTIVATE, softKeyboardEventHandler);
			
			/*_stageText.removeEventListener(Event.CHANGE, stageTextHandler);
			_stageText.removeEventListener(FocusEvent.FOCUS_IN, stageTextHandler);
			_stageText.removeEventListener(FocusEvent.FOCUS_OUT, stageTextHandler);
			_stageText.removeEventListener(KeyboardEvent.KEY_DOWN, stageTextHandler);
			_stageText.removeEventListener(KeyboardEvent.KEY_UP, stageTextHandler);*/
			
			//removeEventListener(MouseEvent.MOUSE_DOWN, clickHandler);
			removeEventListener(TouchEvent.TOUCH_TAP, clickHandler);
			
			// Virer le champ texte
			_stageText.stage = null;
			//_stageText.viewPort = null;
			_stageText = null;
		}
	}
}